package org.dicr.sys.linux;

import java.io.*;
import java.util.*;

import org.apache.log4j.*;
import org.dicr.util.data.exc.*;

/**
 * PapFile parser &amp; compiler
 * 
 * @author Igor A Tarasov, &lt;java@dicr.org&gt;
 * @version 1.0
 */
public class PapFileParser {

	private static final org.apache.log4j.Logger log = Logger.getLogger(PapFileParser.class);

	/** Current line */
	protected char[] chars = null;

	/** Current position */
	protected int pos = 0;

	/**
	 * Constructor
	 */
	public PapFileParser() {
		super();
	}

	/**
	 * Read next word from current line.
	 * 
	 * @return next word
	 * @throws FileFormatException
	 */
	protected synchronized String getNextWord() throws FileFormatException {
		if (this.chars == null) throw new IllegalStateException("null current line");
		if (this.pos < 0 || this.pos > this.chars.length) throw new IllegalStateException("invalid current position");
		if (this.pos == this.chars.length) return null;
		char quote = 0;
		if (this.chars[this.pos] == '\'' || this.chars[this.pos] == '"') {
			quote = this.chars[this.pos];
			this.pos++;
		}
		final StringBuilder word = new StringBuilder();
		while (this.pos < this.chars.length) {
			if (this.chars[this.pos] == '\\') {
				this.pos++;
				if (this.pos >= this.chars.length) throw new FileFormatException(null, new String(this.chars));
				word.append(this.chars[this.pos++]);
				continue;
			} else if (quote != 0) {
				if (this.chars[this.pos] == quote) {
					quote = 0;
					this.pos++;
					break;
				}
				word.append(this.chars[this.pos++]);
				continue;
			} else if (Character.isWhitespace(this.chars[this.pos])) {
				break;
			} else {
				word.append(this.chars[this.pos++]);
			}
		}
		if (quote != 0) throw new FileFormatException(new String(this.chars));
		return word.toString();
	}

	/**
	 * Parse file
	 * 
	 * @param reader
	 *            input file reader
	 * @return parsed elements
	 * @throws IOException
	 */
	public synchronized Map<String, PapEntry> parse(final Reader reader) throws IOException {
		if (reader == null) throw new IllegalArgumentException("null reader");
		final BufferedReader in = new BufferedReader(reader);
		final Map<String, PapEntry> entries = new HashMap<>();
		String line = null;
		while (true) {
			line = in.readLine();
			if (line == null) break;
			line = line.trim();
			if (line.length() < 1 || line.startsWith("#")) continue;
			this.chars = line.toCharArray();
			this.pos = 0;
			try {
				String name = null;
				String server = null;
				String secret = null;
				String ips = null;
				// get name
				String word = this.getNextWord();
				if (PapEntry.ANY.equals(word)) word = null;
				name = word;
				// skip spaces
				if (this.pos >= this.chars.length || !Character.isWhitespace(this.chars[this.pos])) throw new FileFormatException(
						line);
				while (Character.isWhitespace(this.chars[this.pos]) && this.pos < this.chars.length) {
					this.pos++;
				}
				// get server
				word = this.getNextWord();
				if (PapEntry.ANY.equals(word)) word = null;
				server = word;
				// skip spaces
				if (this.pos >= this.chars.length || !Character.isWhitespace(this.chars[this.pos])) throw new FileFormatException(
						line);
				while (Character.isWhitespace(this.chars[this.pos]) && this.pos < this.chars.length) {
					this.pos++;
				}
				// get secret
				word = this.getNextWord();
				if (word.length() < 1) word = null;
				secret = word;
				// get rest
				while (this.pos < this.chars.length && Character.isWhitespace(this.chars[this.pos])) {
					this.pos++;
				}
				if (this.pos < this.chars.length) {
					word = new String(this.chars, this.pos, this.chars.length - this.pos).trim();
					if (word.length() < 1 || "-".equals(word)) word = null;
					ips = word;
					if (PapEntry.ANY.equals(ips)) ips = null;
				}
				final PapEntry entry = new PapEntry(name, server, secret, ips);
				entries.put(entry.getKey(), entry);
			} catch (final FileFormatException ex) {
				PapFileParser.log.warn("pap-file format error", ex);
			}
		}
		in.close();
		PapFileParser.log.trace("loaded " + entries.size() + " entries");
		return entries;
	}

	/**
	 * Выводит данные в формате
	 * 
	 * @param entries
	 *            элементы для записи.
	 * @param ou
	 *            output file writer
	 */
	public synchronized void compile(final Collection<PapEntry> entries, final PrintStream ou) {
		if (entries == null) throw new IllegalArgumentException("null entries");
		if (ou == null) throw new IllegalArgumentException("null output");
		for (final PapEntry entry : entries) {
			String name = entry.getName();
			if (name == null) name = PapEntry.ANY;
			String server = entry.getServer();
			if (server == null) server = PapEntry.ANY;
			String secret = entry.getSecret();
			if (secret == null) secret = "";
			String ips = entry.getIps();
			if (ips == null) ips = PapEntry.ANY;
			ou.printf("%s\t%s\t\"%s\"\t%s\n", name, server, secret, ips);
		}
	}
}
